//
//  YQJSBridge.m
//  CusWebView
//
//  Created by yeqiang on 2024/2/19.
//

#import "YQJSBridge.h"

static NSString *const kJSMessageHandlerForName = @"native";
static NSString *const kNativeCallBack = @"nativeCallBack";
@interface YQJSBridge ()

@property (nonatomic, strong) NSMutableDictionary *msgHandlers;
@property (nonatomic, weak) id <WKNavigationDelegate> webViewDelegate;

@end

@implementation YQJSBridge

+ (instancetype)bridgeForWebView:(WKWebView *)webView {
    YQJSBridge *bridge = [[YQJSBridge alloc] init];
    [bridge setupInstance:webView];
    return bridge;
}

- (void)setupInstance:(WKWebView *)webView {
    self.webView = webView;
    self.webView.navigationDelegate = self;
    [self.webView.configuration.userContentController addScriptMessageHandler:self name:kJSMessageHandlerForName];
    [self setupJSCallBack];
}

- (instancetype)init {
    self = [super init];
    if (self) {
        [self setupJSCallBack];
        [self initStub];
    }
    return self;
}

- (void)dealloc {
    [self deallocStub];
}

- (void)deallocStub {
    
}

- (void)initStub {
}

- (void)registerHandler:(NSString *)handlerName handler:(YQJSHandler)handler {
    self.msgHandlers[handlerName] = [handler copy];
}

- (void)setWebViewDelegate:(id<WKNavigationDelegate>)webViewDelegate {
    _webViewDelegate = webViewDelegate;
}

- (void)setupJSCallBack {
    __weak typeof (self) weakself = self;
    self.jsCallBack = ^(NSString * _Nonnull callBackId, NSDictionary * _Nonnull response) {
        __strong typeof (self) strongself = weakself;
        if ([NSThread isMainThread]) {
            [strongself excuteJSCallBackId:callBackId result:response];
        } else {
            dispatch_async(dispatch_get_main_queue(), ^{
                [strongself excuteJSCallBackId:callBackId result:response];
            });
        }
    };
}

// 执行 js 回调
- (void)excuteJSCallBackId:(NSString *)callBackId result:(NSDictionary *)result {
    if (callBackId && callBackId.length > 0) {
        NSMutableDictionary *composeResult = result.mutableCopy;
        composeResult[@"callbackId"] = callBackId;
        NSString *jsFuncString = [NSString stringWithFormat:@"onCallBack(%@)", composeResult.modelToJSONString];
        [self.webView evaluateJavaScript:jsFuncString completionHandler:^(id _Nullable e, NSError * _Nullable error) {
            if (error) {
                NSLog(@"onCallBack error: %@", error);
            }
        }];
    }
}

// 加载js桥接文件
- (void)loadJSBridgeFile {
    NSString *jsPath = [[NSBundle mainBundle] pathForResource:@"MarsJSBridge" ofType:@"js"];
    NSData *data = [NSData dataWithContentsOfFile:jsPath];
    NSString *dataStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    [self.webView evaluateJavaScript:dataStr completionHandler:^(id _Nullable e, NSError * _Nullable error) {
        if (error) {
            NSLog(@"loadJSBridgeFile error: %@", error);
        }
    }];
}

- (NSMutableDictionary *)msgHandlers {
    if (!_msgHandlers) {
        _msgHandlers = [NSMutableDictionary new];
    }
    return _msgHandlers;
}

- (void)userContentController:(nonnull WKUserContentController *)userContentController didReceiveScriptMessage:(nonnull WKScriptMessage *)message {
    NSLog(@"js交互参数, %@", message.body);
    if ([message.name isEqualToString:kJSMessageHandlerForName]) {
        if ([message.body isKindOfClass:[NSDictionary class]]) {
            dispatch_async(dispatch_get_main_queue(), ^{
                NSString *methodName = [message.body objectForKey:@"methodName"];
                SEL methodSel = NSSelectorFromString(methodName);
                NSMutableDictionary *params = [[NSMutableDictionary alloc] initWithDictionary:message.body];
                if ([self respondsToSelector:methodSel]) {
                    [params removeObjectForKey:@"methodName"];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "=Warc-performSelector-leaks"
                    [self performSelector:methodSel withObject:params];
#pragma clang diagnostic pop
                } else if ([self.msgHandlers objectForKey:methodName]){
                    YQJSHandler handler = self.msgHandlers[methodName];
                    handler(params);
                } else {
                    NSLog(@"%@('%@')", @"methodNotFound", [methodName stringByReplacingOccurrencesOfString:@":" withString:@""]);
                    [self excuteJSCallBackId:params[kNativeCallBack] result:params];
                }
            });
        }
    }
}


#pragma mark - WKNavigationDelegate

- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    [self loadJSBridgeFile];
    if (self.webViewDelegate && [self.webViewDelegate respondsToSelector:@selector(webView:didFinishNavigation:)]) {
        [self.webViewDelegate webView:webView didFinishNavigation:navigation];
    }
}

- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
    if (webView != _webView) return;
    
    decisionHandler(WKNavigationResponsePolicyAllow);
}


- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
    completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
}

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    if (webView != _webView) return;
    // 拦截，如电话
    decisionHandler(WKNavigationActionPolicyAllow);
}

- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {
    
}

- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error {
    
}

@end
